class KeyframesToCanvas {
	/**
	 * 简单的序列帧动画工具
	 * 作者：符先生Fcy
	 * V 1.7
	 * @param {String} nodeName - 节点ID或者Class类名
	 * @param {Array} imageArray - 动画序列帧数组
	 * @param {Object} options - 配置信息，支持
	 *                           speed(播放速度)、x(x位置)、y(y位置)、width(宽)、height(高)、loop(是否循环播放)、scX(水平缩放)、scY(垂直缩放)
	 *                           reverse(是否正序/倒序)、
	 *                           startCallback回调(返回当前canvas实例)、
	 *                           middleCallback回调(返回当前播放中动画的帧数)、
	 *                           endCallback回调(返回当前canvas实例)
	 */
	constructor(nodeName, imageArray, options) {
		this.newObj = Object.prototype.toString
		//判断节点名称类型
		this.res = this.judgeTypeResult(nodeName);
		//判断节点是否存在
		this.isNode = this.judgeNodeExist(nodeName);
		//判断传入数据是否是数组
		this.isArr = this.judgeArrayResult(imageArray);
		//节点名称
		this.nodeName = nodeName;
		//动画速度
		this.speed = options.speed || 60;
		//原点x轴位置
		this.x = options.x || options.width / 2;
		//原点y轴位置
		this.y = options.y || options.height / 2;
		//画布宽度
		this.w = options.width || 300;
		//画布高度
		this.h = options.height || 300;
		//是否正/倒序播放
		this.reverse = options.reverse || false;
		//是否循环播放
		this.loop = options.loop || false;
		//水平缩放
		this.scX = options.scX || 1
		//垂直缩放
		this.scY = options.scY || 1
		//动画开始回调
		this.startCallback = options.startCallback || function() {};
		//动画结束回调
		this.endCallback = options.endCallback || function() {};
		//动画执行中回调
		this.middleCallback = options.middleCallback || function() {};
		//暴露给外部使用的启动方法
		this.start = (callback = function() {}) => {
			this.startAnimation()
			callback.call(this)
		}
		//暴露给外部使用的执行中方法
		this.active = function() {}
		//暴露给外部使用的停止方法
		this.stop = (callback = function() {}) => {
			this.stopAnimation()
			callback.call(this)
		}
		//动画帧总长
		this.fps = 0;
		//动画帧索引
		this.index = 0;
		//指定帧数
		this.identity = '';
		//动画帧单张图片名,为了动态获取对象key
		this.name = [];
		//canvas实例
		this.ctx = null;
		//canvas的dom节点
		this.myCanvas = null;
		//全局定时器
		this.timer = null;
		//动画帧数据
		this.imageArray = imageArray;
		//处理后动画帧对象
		this.newImgObj = {};
		//运行入口判断函数
		this.judgeNodeOrData()
	}
	/**
	 * 入口函数
	 */
	judgeNodeOrData(){
		if (!this.nodeName) {
			console.error('nodeName is undefined')
			return
		}
		if (!this.isArr) {
			console.error('Data type error , I need [Array]')
			return
		}
		if (this.imageArray.length === 0) {
			console.error('The ImageArray Cannot be Empty')
			return
		}
		if (this.res) {
			if (this.isNode) {
				this.node = document.querySelector(this.nodeName)
				this.init()
			} else {
				console.error('The node does not exist')
				return
			}
		} else {
			console.error('node type error , i need [String]')
			return
		}
	}
	/**
	 * 初始化canvas
	 */
	async init() {
		try {
			let that = this;
			this.ctx = await this.insetCanvasNode();
			this.newImgObj = await this.imageObject(this.imageArray)
			this.fps = await Object.keys(this.newImgObj).length
			this.index = this.reverse ? (this.fps - 1) : 0;
		} catch (e) {
			console.error(e)
		}
	}
	/**
	 * 处理图片对象
	 */
	imageObject(arr) {
		return new Promise(resolve=>{
			let imgObj = {};
			let s = this;
			for (let i = 0; i < arr.length; i++) {
				// console.log('图片读取中...', `${parseInt((i/(arr.length-1))*100)}%`)
				imgObj[`${arr[i].name}-${i}`] = new Image();
				imgObj[`${arr[i].name}-${i}`].src = arr[i].src;
				imgObj[`${arr[i].name}-${i}`].identity = '';
				imgObj[`${arr[i].name}-${i}`].onload = function() {
					s.renderImage()
				}
				this.name.push(`${arr[i].name}-${i}`)
			}
			resolve(imgObj)
		})
	}
	/**
	 * 插入canvas节点
	 */
	insetCanvasNode() {
		return new Promise(resolve=>{
			let canvasNode = document.createElement('canvas');
			this.node.style.zIndex = '2000'
			canvasNode.id = 'pop-canvas';
			canvasNode.width = this.w;
			canvasNode.height = this.h;
			this.node.appendChild(canvasNode)
			this.myCanvas = canvasNode
			let context = canvasNode.getContext('2d')
			context.translate(this.x, this.y)
			context.scale(this.scX,this.scY)
			resolve(context)
		})
	}
	/**
	 * 启动动画
	 */
	startAnimation() {
		if (this.fps <= 1) {
			return
		} else {
			let s = this;
			clearInterval(s.timer)
			this.startCallback(this)
			if (this.reverse) {
				s.timer = setInterval(function() {
					s.middleCallback(s.index)
					s.active(s.index)
					s.fps--
					s.index--
					s.ctx.clearRect(0, 0, s.node.clientWidth, s.node.clientHeight)
					s.renderImage();
					if (s.fps <= 1) {
						if(s.loop){
							s.resetStart()
						}else{
							s.stopAnimation()
						}
					}
					if(s.identity == s.newImgObj[s.name[s.index]].identity && s.identity){
						s.stopAnimation()
					}
				}, s.speed)
			} else {
				s.timer = setInterval(function() {
					s.middleCallback(s.index)
					s.active(s.index)
					s.fps--
					s.index++
					s.ctx.clearRect(0, 0, s.node.clientWidth, s.node.clientHeight)
					s.renderImage();
					if (s.fps <= 1) {
						if(s.loop){
							s.resetStart()
						}else{
							s.stopAnimation()
						}
					}
					if(s.identity == s.newImgObj[s.name[s.index]].identity && s.identity){
						s.stopAnimation()
					}
				}, s.speed)
			}
		}
	}
	
	/**
	 * 从指定帧数开始播放
	 * @param {Number} frames - 指定帧数
	 */
	goToAndPlay(frames = 0) {
		let arrLength = Object.keys(this.newImgObj).length
		if (typeof frames === 'number') {
			if (frames < 0) {
				console.error('帧数不能小于0')
				return
			}
			if (frames > arrLength) {
				console.error('帧数不能大于图片数据总长')
				return
			}
			if (this.reverse) {
				this.index = arrLength - frames;
				this.fps = arrLength - frames;
				this.startAnimation()
			} else {
				this.index = frames;
				this.fps = arrLength - frames;
				this.startAnimation()
			}
		} else {
			console.error('Type error,The param is not Number')
		}

	}
	/**
	 * 从指定帧数停止
	 * @param {Number} frames - 指定帧数
	 */
	goToAndStop(frames) {
		if(typeof frames === 'number'){
			let arrLength = Object.keys(this.newImgObj).length
			if (frames < 0) {
				console.error('帧数不能小于0')
				return
			}
			if (frames > arrLength) {
				console.error('帧数不能大于图片数据总长')
				return
			}
			if(!frames){
				return
			}else{
				this.identity = frames
				this.newImgObj[this.name[frames]].identity = frames	
			}
		}else{
			console.error('Type error,The type is Number')
		}
	}
	/**
	 * 停止动画
	 */
	stopAnimation() {
		this.endCallback(this)
		clearInterval(this.timer)
	}
	/**
	 * 重置动画
	 */
	reset() {
		this.endCallback(this)
		clearInterval(this.timer)
		this.fps = Object.keys(this.newImgObj).length
		this.index = this.reverse ? (this.fps - 1) : 0;
		this.ctx.clearRect(0, 0, this.node.clientWidth, this.node.clientHeight)
		this.renderImage()
	}
	/**
	 * 重新播放动画
	 */
	resetStart(){
		this.endCallback(this)
		this.fps = Object.keys(this.newImgObj).length
		this.index = this.reverse ? (this.fps - 1) : 0;
		this.ctx.clearRect(0, 0, this.node.clientWidth, this.node.clientHeight)
		this.renderImage()
	}
	/**
	 * 写入缩放
	 * @param {Number} dx - 水平缩放因子
	 * @param {Number} dy - 垂直缩放因子  
	 */
	setZoom(dx,dy){
		if(typeof dx != 'number' || typeof dy != 'number'){
			console.error('Incorrect parameter,It should be "number"')
			return
		}
		if(dx == 0 || dy == 0){
			return
		}
		this.ctx.clearRect(0, 0, this.node.clientWidth, this.node.clientHeight)
		this.ctx.scale(dx,dy)
		this.renderImage()
	}
	/**
	 * 写入宽度
	 * @param {Number} width - 需要修改的画布宽度
	 */
	setWidth(width){
		if(typeof width != 'number'){
			console.error('Incorrect parameter,It should be "number"')
			return
		}
		if(width == 0){
			return
		}
		this.node.setAttribute('style',`width: ${width}px; height: ${this.node.clientHeight}px`)
		this.myCanvas.setAttribute('style',`width: ${width}px; height: ${this.node.clientHeight}px`)
		this.ctx.clearRect(0, 0, this.node.clientWidth, this.node.clientHeight)
		this.renderImage()
	}
	/**
	 * 写入高度
	 * @param {Number} height - 需要修改的画布高度
	 */
	setHeight(height){
		if(typeof height != 'number'){
			console.error('Incorrect parameter,It should be "number"')
			return
		}
		if(height == 0){
			return
		}
		this.node.setAttribute('style',`width: ${this.node.clientWidth}px; height: ${height}px`)
		this.myCanvas.setAttribute('style',`width: ${this.node.clientWidth}px; height: ${height}px`)
		this.ctx.clearRect(0, 0, this.node.clientWidth, this.node.clientHeight)
		this.renderImage()
	}
	/**
	 * 渲染图像
	 */
	renderImage() {
		this.ctx.save();
		this.ctx.drawImage(this.newImgObj[this.name[this.index]], 0, 0)
		this.ctx.restore()
	}
	/**
	 * 判断节点是否存在
	 * @param {String} param - 节点名称
	 */
	judgeNodeExist(param) {
		let node = document.querySelector(param)
		return node === null ? false : true;
	}
	/**
	 * 判断节点名称类型是否为字符串
	 * @param {Any} param - 节点名称
	 */
	judgeTypeResult(param) {
		let type = this.newObj.call(param)
		return type === "[object String]"
	}
	/**
	 * 判断动画帧数据类型是否为数组
	 * @param {Any} param - 动画帧
	 */
	judgeArrayResult(param) {
		let type = this.newObj.call(param)
		return type === "[object Array]"
	}
}
